package book;

// 排序算法解决方案: SortingSolution 类在 2.4.1 至 2.4.5 中通用
public class SortingSolution {
    // 比较两个数组元素大小
    int comp(int a, int b) { return Integer.compare(a, b); }

    // 交换数组元素
    void swap(int[] a, int i, int j) { int temp = a[i]; a[i] = a[j]; a[j] = temp; }

    // 冒泡排序
    void bubbleSort(int[] a) {
        int bound, exchange = a.length - 1;
        while (exchange != 0) {
            bound = exchange;
            exchange = 0;
            for (int i = 0; i < bound; i++) 
                if (comp(a[i], a[i + 1]) > 0) {
                    swap(a, i, i + 1);
                    exchange = i;
                }
        }
    }

    // 选择排序
    void selectSort(int[] a) {
        for (int i = 0; i < a.length - 1; i++) {
            int index = i;
            for (int j = i + 1; j < a.length; j++) 
                if (comp(a[j], a[index]) < 0)
                    index = j;
            if (index != i)
                swap(a, i, index);
        }
    }

    // 插入排序
    void insertSort(int[] a) {
        for (int i = 1; i < a.length; i++) {// 从第 2 个记录开始执行
            int t = a[i], j = i - 1;		// 将待插入元素暂存于变量 t 
            for (; j >= 0 && t < a[j]; j--) // 搜索插入位置，注意不要越界
                a[j + 1] = a[j];			// 记录后移
            a[j + 1] = t;					// 完成 t 的插入操作
        }
    }

    // 归并排序
    int[] mergeSort(int[] a, int left, int right) {
        // 边界条件：没有元素或 1 个元素直接返回
        if (left > right) return new int[0];
        if (left == right) return new int[] { a[left] };

        // 划分并求解子问题
        int mid = left + (right - left) / 2;
        int[] a1 = mergeSort(a, left, mid);
        int[] a2 = mergeSort(a, mid + 1, right);

        // 合并
        int[] c = new int[a1.length + a2.length]; 
        int i1 = 0, i2 = 0;
        while (i1 < a1.length && i2 < a2.length) {
            if (a1[i1] <= a2[i2]) {
                c[i1 + i2] = a1[i1];
                i1++;
            } else {
                c[i1 + i2] = a2[i2];
                i2++;
            }
        }

        // 不要忘记对子数组的剩余部分进行合并

        while (i1 < a1.length) {
            c[i1 + i2] = a1[i1];
            i1++;
        }

        while (i2 < a2.length) {
            c[i1 + i2] = a2[i2];
            i2++;
        }

        return c;
    }

    // （快速排序核心算法）对 a[p..r] 进行划分操作
    int partition(int[] a, int p, int r) {
        int pivot = a[p];   // 选定轴值
        int i = p, j = r;   // 初始化左右指针
        while (i < j) {
            while (i < j && pivot <= a[j])
                j--;
            if (i < j) {
                swap(a, i, j);
                i++;
            }
            while (i < j && pivot >= a[i])
                i++;
            if (i < j) {
                swap(a, i, j);
                j--;
            }
        }
        return i;
    }

    // 快速排序
    void quickSort(int[] a, int p, int r) {
        if (p < r) {
            int q = partition(a, p, r); // 划分，并返回轴值在数组中的位置
            quickSort(a, p, q - 1);     // 递归求解左子数组
            quickSort(a, q + 1, r);     // 递归求解右子数组
        }
    }

    // 测试：应用排序算法对序列进行排序
    int[] testSort(int[] a) {
        // 调用其中一种排序算法：
        int testMethod = 3;

        // 在后续章节中，修改下面语句以调用其它排序算法
        switch (testMethod) {
            case 1:
                bubbleSort(a); // 冒泡排序
                break;
            case 2:
                selectSort(a); // 选择排序
                break;
            case 3:
                insertSort(a); // 插入排序
                break;
            case 4:
                a = mergeSort(a, 0, a.length - 1);    // 归并排序
                break;
            case 5:
                quickSort(a, 0, a.length - 1); // 快速排序
                break;
        }
        return a;
    }

    // 打印数组到输出终端
    static String display(int[] a, int i) {
        return i == a.length - 1 ? "" + a[i]: 
            String.format("%d\t%s", a[i], display(a, i + 1));
    }

    public static void main(String[] args) {
        int[] a = { 49, 12, 54, 96, 26, 37, 48, 64 };
        System.out.println("排序前：" + display(a, 0));
        SortingSolution sln = new SortingSolution();
        a = sln.testSort(a);
        System.out.println("排序后：" + display(a, 0));
    }
}
